//! Represents information relating to function unwinding.
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "unwind")]
pub mod systemv;

#[cfg(feature = "unwind")]
pub mod winx64;

/// Represents unwind information for a single function.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum UnwindInfo {
    /// Windows x64 ABI unwind information.
    #[cfg(feature = "unwind")]
    WindowsX64(winx64::UnwindInfo),
    /// System V ABI unwind information.
    #[cfg(feature = "unwind")]
    SystemV(systemv::UnwindInfo),
}

/// Intermediate representation for the unwind information
/// generated by a backend.
pub mod input {
    use crate::binemit::CodeOffset;
    use alloc::vec::Vec;
    #[cfg(feature = "enable-serde")]
    use serde::{Deserialize, Serialize};

    /// Elementary operation in the unwind operations.
    #[derive(Clone, Debug, PartialEq, Eq)]
    #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
    pub enum UnwindCode<Reg> {
        /// Defines that a register is saved at the specified offset.
        SaveRegister {
            /// The saved register.
            reg: Reg,
            /// The specified offset relative to the stack pointer.
            stack_offset: u32,
        },
        /// Defines that a register is as defined before call.
        RestoreRegister {
            /// The restored register.
            reg: Reg,
        },
        /// The stack pointer was adjusted to allocate the stack.
        StackAlloc {
            /// Size to allocate.
            size: u32,
        },
        /// The stack pointer was adjusted to free the stack.
        StackDealloc {
            /// Size to deallocate.
            size: u32,
        },
        /// The alternative register was assigned as frame pointer base.
        SetFramePointer {
            /// The specified register.
            reg: Reg,
        },
        /// Restores a frame pointer base to default register.
        RestoreFramePointer,
        /// Saves the state.
        RememberState,
        /// Restores the state.
        RestoreState,
    }

    /// Unwind information as generated by a backend.
    #[derive(Clone, Debug, PartialEq, Eq)]
    #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
    pub struct UnwindInfo<Reg> {
        /// Size of the prologue.
        pub prologue_size: CodeOffset,
        /// Unwind codes for prologue.
        pub prologue_unwind_codes: Vec<(CodeOffset, UnwindCode<Reg>)>,
        /// Unwind codes for epilogues.
        pub epilogues_unwind_codes: Vec<Vec<(CodeOffset, UnwindCode<Reg>)>>,
        /// Entire function size.
        pub function_size: CodeOffset,
        /// Platform word size in bytes.
        pub word_size: u8,
        /// Initial stack pointer offset.
        pub initial_sp_offset: u8,
    }
}
